const _path = require('path')
const userid = require('userid')
const PluginResolve = require('./pluginResolve').PluginResolve
const PluginError = require('./pluginResolve').PluginError
const ConfigError = require('./configError')

function set (config, data) {
  setPlugin(config, data.plugin)
  setToken(config, data.token)
  setBranches(config, data.branches)
  setUid(config, data.user)
  setGid(config, data.group)
  setWorkingDirectory(config, data.workingDirectoryBase, data.workingDirectory)
  setCommand(config, data.command, data.commandFileBase, data.commandFile)
  setLogFile(config, data.logFileBase, data.logFile)
}

function setPlugin (config, pluginName) {
  if (pluginName) {
    try {
      config.normalizeData = PluginResolve(pluginName)
    } catch (err) {
      if (err instanceof PluginError) {
        throw new ConfigError(err.message)
      }
    }
  } else {
    throw new ConfigError('必须指定一个插件')
  }
}

function setToken (config, token) {
  if (token) {
    config.token = token
  }
}

function setBranches (config, branches) {
  config._branches = branches || '*'
}

function setUid (config, user) {
  config._uid = resolveUserId({
    user: user,
    name: 'user',
    idFunc: 'uid',
    nameFunc: 'username'
  })
}

function setGid (config, group) {
  config._gid = resolveUserId({
    user: group,
    name: 'group',
    idFunc: 'gid',
    nameFunc: 'groupname'
  })
}

function setWorkingDirectory (config, workingDirectoryBase, workingDirectory) {
  workingDirectory = workingDirectory || config._path
  let resolved = resolvePath(workingDirectoryBase, workingDirectory, {
    childRequired: true,
    childMissingError: 'workingDirectory必须得配置',
    baseMissingError: 'workingDirectory配置成相对路径，但没有给出workingDirectoryBase',
    baseRelativeError: 'workingDirectoryBase必须配置成绝对路径'
  })
  if (resolved.error) {
    throw new ConfigError(resolved.error)
  } else {
    config._workingDirectory = resolved.path
  }
}

function setCommand (config, command, commandFileBase, commandFile) {
  if (command) {
    config._command = command
  } else {
    commandFile = commandFile || `${config._path}`
    let resolved = resolvePath(commandFileBase, commandFile, {
      childRequired: true,
      childMissingError: 'command和commandFile必须配置其一',
      baseMissingError: 'commandFile配置成相对路径，但没有给出commandFileBase',
      baseRelativeError: 'commandBase必须配置成绝对路径'
    })
    if (resolved.error) {
      throw new ConfigError(resolved.error)
    } else {
      config._command = resolved.path
    }
  }
}

function setLogFile (config, logFileBase, logFile) {
  logFile = logFile === true ? `${config._path}.log` : logFile
  let resolved = resolvePath(logFileBase, logFile, {
    baseMissingError: 'logFile配置成相对路径，但没有给出logFileBase',
    baseRelativeError: 'logFileBase必须配置成绝对路径'
  })
  if (resolved.error) {
    throw new ConfigError(resolved.error)
  } else if (resolved.path) {
    config._logFile = resolved.path
  }
}

function existFunc (value, funcName, paramName) {
  try {
    userid[funcName](value)
    return true
  } catch (err) {
    if (err.message === `${paramName} not found`) {
      return false
    } else {
      throw err
    }
  }
}

const exists = {
  uid: function (uid) {
    return existFunc(uid, 'username', 'uid')
  },
  gid: function (gid) {
    return existFunc(gid, 'groupname', 'gid')
  },
  username: function (username) {
    return existFunc(username, 'uid', 'username')
  },
  groupname: function (groupname) {
    return existFunc(groupname, 'gid', 'groupname')
  }
}

function resolveUserId (options) {
  let { user, name, idFunc, nameFunc } = options
  if (user === undefined) {
    throw new ConfigError(`${name}是必配项`)
  }
  if (typeof (user) === 'number') {
    if (exists[idFunc](user)) {
      return userid[nameFunc](user)
    } else {
      throw new ConfigError(`${name}不存在`)
    }
  } else if (typeof (user) === 'string') {
    if (exists[nameFunc](user)) {
      return userid[idFunc](user)
    } else {
      throw new ConfigError(`${name}不存在`)
    }
  } else {
    throw new ConfigError(`${name}必须是字符串或数字类型`)
  }
}

function resolvePath (base, child, options = {}) {
  if (child === undefined) {
    if (options.childRequired) {
      return { error: options.childMissingError }
    } else {
      return {}
    }
  }
  if (child[0] === '/') {
    return { path: child }
  }
  if (base === undefined) {
    return { error: options.baseMissingError }
  }
  if (base[0] !== '/') {
    return { error: options.baseRelativeError }
  }
  return { path: _path.resolve(base, child) }
}

exports.set = set
